package com.ng.framework.view.desktop;

import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;
import java.util.List;

import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;

import org.apache.commons.collections.CollectionUtils;

import com.ng.framework.persistence.NGGenericPersistence;

public abstract class NGTablePanel<T, P extends NGGenericPersistence<T>> extends JPanel
		implements
			ActionListener,
			ListSelectionListener {

	private static final long serialVersionUID = 5338872454312823150L;

	private P persistence;
	protected NGEntitySelectorField<T, P> entitySelection;

	private JTable table;
	private JButton buttonNuevo;
	private JButton buttonModificar;
	private JButton buttonEliminar;
	private JButton buttonSeleccionar;
	private NGDefaultTableModel tableModel;
	private TableCellRenderer headerRenderer = new NGTableHeaderRenderer();
	private TableCellRenderer cellRenderer = new NGTableCellRenderer();
	private Border headerBorder = UIManager.getBorder("TableHeader.cellBorder");

	public NGTablePanel(P persistence) {
		this.persistence = persistence;

		initComponents();

		buildGui();
	}

	public NGTablePanel(P persistence, NGEntitySelectorField<T, P> entitySelection) {
		this.persistence = persistence;
		this.entitySelection = entitySelection;

		initComponents();

		buildGui();
	}

	private void initComponents() {
		this.tableModel = new NGDefaultTableModel(this.getColumnNames());

		this.table = new JTable(tableModel);

		this.table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
		int[] columnWidths = getColumnWidths();
		for (int i = 0; i < columnWidths.length; i++) {
			TableColumn column = table.getColumnModel().getColumn(i);
			column.setPreferredWidth(columnWidths[i]);

			JLabel headerLabel = new JLabel(String.valueOf(column.getHeaderValue()), null, JLabel.CENTER);
			headerLabel.setBorder(headerBorder);

			column.setHeaderValue(headerLabel);

			column.setHeaderRenderer(headerRenderer);
			column.setCellRenderer(cellRenderer);

		}

		table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		table.getSelectionModel().addListSelectionListener(this);

		this.buttonNuevo = NGGUIUtils.createIconButton("icons/plus.png", this);
		this.buttonModificar = NGGUIUtils.createIconButton("icons/pencil.png", this);
		buttonModificar.setEnabled(false);
		if (entitySelection == null) {
			this.buttonEliminar = NGGUIUtils.createIconButton("icons/cross.png", this);
			buttonEliminar.setEnabled(false);
		} else {
			this.buttonSeleccionar = NGGUIUtils.createIconButton("icons/check.png", this);
			buttonSeleccionar.setEnabled(false);
		}
	}

	private void buildGui() {
		this.setLayout(new GridBagLayout());

		this.add(new JScrollPane(table, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED),
				new GridBagConstraints(0, 0, 3, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 5,
						0), 0, 0));

		this.add(buttonNuevo, new GridBagConstraints(0, 1, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.NONE,
				new Insets(0, 0, 0, 5), 0, 0));
		this.add(buttonModificar, new GridBagConstraints(1, 1, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.NONE,
				new Insets(0, 0, 0, 5), 0, 0));
		if (entitySelection == null) {
			this.add(buttonEliminar, new GridBagConstraints(2, 1, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.NONE,
					new Insets(0, 0, 0, 0), 0, 0));
		} else {
			this.add(buttonSeleccionar, new GridBagConstraints(2, 1, 1, 1, 1, 0, GridBagConstraints.WEST,
					GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
		}
	}

	public final void setData(List<T> data) {
		tableModel.setRowCount(0);

		if (CollectionUtils.isNotEmpty(data)) {
			for (T dataObject : data) {
				Object[] row = this.toRow(dataObject);
				if(row != null) {
					tableModel.addRow(row);					
				}
			}
		}
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == buttonNuevo) {
			if (this.create(persistence)) {
				this.setData(persistence.getAll());
			}
		} else if (e.getSource() == buttonModificar) {
			if (this.edit(persistence, this.getSelectedInstance(persistence, this.getSelectedRowData()))) {
				this.setData(persistence.getAll());
			}
		} else if (e.getSource() == buttonEliminar) {
			if(this.esBorrable(this.getSelectedInstance(persistence, this.getSelectedRowData()))) {
				int opcion = JOptionPane.showConfirmDialog(null, "El registro será borrado permanentemente, ¿Desea continuar?",
						"Confirmación de Borrado", JOptionPane.YES_NO_OPTION);
				if (opcion == 0) {
					persistence.remove(this.getSelectedInstance(persistence, this.getSelectedRowData()));
					this.setData(persistence.getAll());
				}				
			}

		} else if (e.getSource() == buttonSeleccionar) {
			entitySelection.setEntity(this.getSelectedInstance(persistence, this.getSelectedRowData()));
		}
	}
	
	protected boolean esBorrable(T instance) {
		return true;
	}

	private Object[] getSelectedRowData() {
		final Object[] rowData = new Object[table.getColumnCount()];

		final int selectedRow = table.getSelectedRow();
		for (int i = 0; i < rowData.length; i++) {
			rowData[i] = table.getValueAt(selectedRow, i);
		}

		return rowData;
	}

	@Override
	public void valueChanged(ListSelectionEvent e) {
		boolean rowSelected = table.getSelectedRowCount() > 0;

		buttonModificar.setEnabled(rowSelected);
		if (entitySelection == null) {
			buttonEliminar.setEnabled(rowSelected);
		} else {
			buttonSeleccionar.setEnabled(rowSelected);
		}
		
	}

	protected abstract Object[] toRow(T dataObject);
	protected abstract String[] getColumnNames();
	
	//45 Menos que el width del frame que lo contiene
	protected abstract int[] getColumnWidths();
	protected abstract boolean edit(P persistence, T instance);
	protected abstract T getSelectedInstance(P persistence, Object[] rowData);
	protected abstract boolean create(P persistence);

	private class NGDefaultTableModel extends DefaultTableModel {
		private static final long serialVersionUID = 1L;

		public NGDefaultTableModel(String[] columnNames) {
			super(columnNames, 0);
		}

		@Override
		public boolean isCellEditable(int row, int column) {
			return false;
		}

	}

	private class NGTableHeaderRenderer implements TableCellRenderer {
		public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row,
				int column) {
			return (JComponent) value;
		}
	}

	private class NGTableCellRenderer extends DefaultTableCellRenderer implements TableCellRenderer {

		private static final long serialVersionUID = 6697977879616419210L;

		public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row,
				int column) {

			super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
			
			this.setIcon(null);

			if (value instanceof Long || value instanceof Integer || value instanceof Date || value instanceof String) {
				return renderCell(this, String.valueOf(value), SwingConstants.CENTER);
			} else if (value instanceof Double || value instanceof Float) {
				return renderCell(this, String.valueOf(value), SwingConstants.RIGHT);
			} else if (value instanceof Icon) {
				this.setText(null);
				this.setIcon((Icon)value);
				this.setHorizontalAlignment(SwingConstants.CENTER);
				return this;
			}

			return new JLabel(String.valueOf(value));
		}

		private JLabel renderCell(JLabel render, String value, int align) {
			render.setText(value);
			render.setHorizontalAlignment(align);
			return render;
		}

	}

}
